home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-05-26 | 16.3 KB | 636 lines | [TEXT/CWIE] |
- /* DebugTerm.c
-
- This file can be included in a C project or compiled as a
- library. It allows the programmer to send debugging
- information through a Communications Toolbox connection
- to any terminal program. If you wish to use the ADSP tool,
- you will want to use it with one that supports the
- Communications Toolbox.
-
- In theory, DebugTerm works with any connection tool. In
- practice, it will probably be used with the ADSP tool to
- send debugging information to a terminal program on the same
- Macintosh (it works!), or to one running on another
- Macintosh somewhere on the network. However, it may
- alternatively be used with the serial tool to send debugging
- information to a terminal program on another computer or to
- a dumb terminal, or perhaps even a serial device such as a
- printer or modem.
-
- Assumptions:
- The connection tool has no "persistent" windows or menus
- that need the support of the host application!
- You will not attempt to send data from the connected
- terminal or device TO Debugterm.
- You are running a machine and OS that has/supports
- Communications Toolbox.
- You will not attempt to cram more into the CTB buffers
- than they can hold.
-
- Problems:
- Can't find a way to tell if the host application has
- already initialized the Communications Toolbox.
- Can't find a way to tell if the user (you) attempts to
- connect synchronously to a process on the same
- machine (won't work).
- It doesn't notice if the connection goes away (such as
- is possible with the ADSP driver).
-
- Following is a summary of external functions used with DebugTerm:
-
- OpenDebugTerm: Used to launch DebugTerm, initiate a
- connection, and prepare to send debugging information to
- the recipient.
-
- CloseDebugTerm: Used to close connection and release
- resources in use by DebugTerm.
-
- DebugTerm: Used to send a Str255 containing debugging
- information to DebugTerm, which it will in turn send
- through the connection to the recipient. (This was the
- original test call.)
-
- debugf: Meant to mimic c the stdio library printf,
- (see K & R, especially 2nd ed. pp. 155 and 156) it uses
- a first parameter that is a format string and an
- arbitrary number of additional parameters which match
- the specifications in the format string.
-
- 1 2 3 4 5 6 7 8
- 12345678901234567890123456789012345678901234567890123456789012345678901234567890
- */
-
- // Necessary includes
- //#include "Connections.h"
- //#include "CommResources.h"
- #include "stdarg.h"
- #include "DebugTerm.h"
-
- // Private literals
- #define kPrefsType 'rsrc' // Call it a ResEdit
- #define kAppSignature 'RSED' // resource file
- #define kClassCM 'cbnd'
- #define rConnClassID 1001
- #define rFirstToolConfigID 2001
-
- // Global variables
- static ConnHandle gConn = nil;
- static Boolean gAsyncCalls, gCTBInited = false;
- static CMFlags gFlags;
-
- //**********************************************************
- //
- // pStrCopy - Copy the value of p1 to p2
- //
- //**********************************************************
- static void pStrCopy(unsigned char *p1, unsigned char *p2)
- {
- register short len = *p2++ = *p1++;
- while (--len >= 0)
- *p2++=*p1++;
- }
-
- //**********************************************************
- //
- // GetOpenFileDir - Gets VRef and ParentDirId for open file
- //
- //**********************************************************
- static OSErr GetOpenFileDir(short openFileRef, short *vRef,
- long *parDirID)
- {
- FCBPBRec pb;
- OSErr theErr;
-
- pb.ioCompletion = nil;
- pb.ioFCBIndx = 0;
- pb.ioVRefNum = 0;
- pb.ioRefNum = openFileRef;
- pb.ioNamePtr = nil;
-
- theErr = PBGetFCBInfo(&pb, false);
-
- *vRef = pb.ioFCBVRefNum;
- *parDirID = pb.ioFCBParID;
-
- return theErr;
- }
-
- //**********************************************************
- //
- // OpenPrefsFile - Opens prefs, creates if not found
- //
- //**********************************************************
- static OSErr OpenPrefsFile(short *myRefNum, Boolean createIt)
- {
- Str255 myFileName;
- long myDirID;
- short myVRef, appResRef;
- OSErr theErr;
- FSSpec mySpec, appDirSpec;
- Boolean found;
-
- *myRefNum = -1;
- found = false;
- pStrCopy("\pDebugTerm Prefs", myFileName);
- appResRef = CurResFile();
-
- theErr = GetOpenFileDir(appResRef, &myVRef, &myDirID);
- if (!theErr) {
- theErr = FSMakeFSSpec(myVRef, myDirID, myFileName, &appDirSpec);
- if (!theErr) {
- found = true;
- BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
- }
- }
-
- if ( (theErr == fnfErr) && createIt ) {
- BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
- FSpCreateResFile(&mySpec, kAppSignature, kPrefsType, 0);
- theErr = ResError();
- }
-
- if (!theErr) {
- *myRefNum = FSpOpenResFile(&mySpec, fsRdWrPerm);
- theErr = ResError();
- }
-
- return theErr;
- }
-
- //**********************************************************
- //
- // CMCompetion - Completion routine for the Connection Mgr
- //
- //**********************************************************
- static pascal void CMCompletion(ConnHandle hConn)
- {
- // Should at least check to see if (**hConn).errCode
- // is non-zero. Perhaps use hConn to put any error
- // code in UserData for the main program to flush out.
- }
-
- //**********************************************************
- //
- // CMSendProc - Sends the data out through the connection
- //
- //**********************************************************
- static pascal OSErr CMSendProc(Ptr thePtr, long theSize, CMFlags flags)
- {
- long count, returnVal;
- EventRecord theEvent;
- CMBufferSizes sizes;
- OSErr theErr;
- CMStatFlags statFlags;
-
- returnVal = 0;
-
- if (gConn) {
- count = theSize; // Give other processes time while
- if (gAsyncCalls) do { // waiting for previous write to finish
- if (theErr = CMStatus(gConn, sizes, &statFlags))
- return theErr;
- if (statFlags & (cmStatusDWPend + cmStatusOpening))
- WaitNextEvent(nullEvent, &theEvent, 1, nil);
- } while (statFlags & (cmStatusDWPend + cmStatusOpening));
- if (theErr = CMWrite(gConn, thePtr, &count, cmData,
- gAsyncCalls, CMCompletion, -1, statFlags))
- return theErr;
- }
- return noErr;
- }
-
- //**********************************************************
- //
- // InitCTBStuff - Initializes comm toolbox
- //
- //**********************************************************
- static OSErr InitCTB(void)
- {
- short theErr;
-
- if (!gCTBInited) {
- if (theErr = InitCTBUtilities())
- return theErr;
- if (theErr = InitCRM())
- return theErr;
- if (theErr = InitCM())
- return theErr;
- gCTBInited = true;
- }
- return noErr;
- }
-
- //**********************************************************
- //
- // FindTool - Tries to get the choice tool if
- // available, else tries first available.
- //
- //**********************************************************
- static OSErr FindTool(short *procID, Str255 toolName)
- {
- Handle rHand;
- StringHandle sH;
- OSErr theErr;
-
- *procID = -1;
- theErr = noErr;
-
- // Get name of tool last used, and its procID
- rHand = GetResource(kAppSignature, rConnClassID);
- if (rHand) {
- sH = (StringHandle)rHand;
- pStrCopy(*sH, toolName);
- *procID = CMGetProcID(toolName);
- }
- if (*procID == -1 ) {
- // may not actually have a resource file open, or the
- // tool we want is no longer in the extensions folder so
- // get the name and procID of the first tool we find
- if (!(theErr = CRMGetIndToolName(kClassCM, 1, toolName)))
- *procID = CMGetProcID(toolName);
- if (*procID == -1)
- theErr = ResError();
- if (!theErr)
- theErr = kToolNotAvailErr;
- }
- return theErr;
- }
-
- //**********************************************************
- //
- // ReadPrefs - Read prefs for tool of specified class
- //
- //**********************************************************
- static OSErr ReadPrefs(short *procID, Handle *configH)
- {
- short resFileRef;
- Str255 toolName;
- Handle resH;
- OSErr theErr;
-
- theErr = OpenPrefsFile(&resFileRef, kDontCreate);
- // if we can't open prefs, we can still get a tool
- theErr = FindTool(procID, toolName);
- *configH = nil;
-
- if (resFileRef != -1) {
- if (!theErr) {
- // if the resource file was opened, get the
- // configuration string from a resource
- resH = GetNamedResource(kAppSignature, toolName);
- if (resH) {
- *configH = resH;
- HandToHand(configH);
- ReleaseResource(resH);
- } else
- theErr = ResError();
- }
- CloseResFile(resFileRef);
- }
- return theErr; // could be returning kToolNotAvailErr
- }
-
- //**********************************************************
- //
- // WritePrefs - Create/Update connection tool prefs
- //
- //**********************************************************
- static OSErr WritePrefs(ConnHandle hConn)
- {
- Ptr specP;
- Handle specH, resH;
- Str255 tName, tClassName;
- OSErr theErr;
- short resID, resFileRef;
- Size hSize;
- StringHandle sH;
-
- if (!hConn)
- return kNoConnectionErr;
-
- // Get the current tool's name and its config string
- CMGetToolName((**hConn).procID, tName);
- specP = CMGetConfig(hConn);
- if (!specP)
- return kGetConfigErr;
-
- if (theErr = OpenPrefsFile(&resFileRef, kDoCreate))
- return theErr;
-
- // Update/Create resource specifying tool name
- resID = rConnClassID;
- pStrCopy("\pConnection Tool Name", tClassName);
- sH = NewString(tName);
- if (sH) {
- hSize = GetHandleSize((Handle)sH);
- resH = Get1Resource(kAppSignature, resID);
- theErr = ResError();
- if ((!resH) && ((theErr == resNotFound) || (theErr == noErr))) {
- AddResource((Handle)sH, kAppSignature, resID, tClassName);
- if (!(theErr = ResError()))
- WriteResource((Handle)sH);
- if (!theErr)
- theErr = ResError();
- ReleaseResource((Handle)sH);
- } else if (resH) {
- SetHandleSize(resH, hSize);
- if (!(theErr = MemError())) {
- BlockMove(*sH, *resH, hSize);
- ChangedResource(resH);
- if (!(theErr = ResError()))
- WriteResource(resH);
- if (!theErr)
- theErr = ResError();
- }
- ReleaseResource(resH);
- }
- DisposeHandle((Handle)sH);
- } else
- theErr = MemError();
-
- // Update/Create resource with the tool config string
- if (!theErr) {
- hSize = GetPtrSize(specP);
- resH = Get1NamedResource(kAppSignature, tName);
- theErr = ResError();
- if ( (!resH) && ((theErr == resNotFound) || (theErr == noErr)) ) {
- theErr = noErr;
- specH = NewHandle(hSize);
- if (specH) {
- BlockMove(specP, *specH, hSize);
- do
- resID = UniqueID(kAppSignature);
- while (resID <= rFirstToolConfigID);
- AddResource(specH, kAppSignature, resID, tName);
- if (!(theErr = ResError()))
- WriteResource(specH);
- if (!theErr)
- theErr = ResError();
- ReleaseResource(specH);
- DisposeHandle(specH);
- } else
- theErr = MemError();
- } else if (resH) {
- SetHandleSize(resH, hSize);
- if (!(theErr = MemError())) {
- BlockMove(specP, *resH, hSize);
- ChangedResource(resH);
- if (!(theErr = ResError()))
- WriteResource(resH);
- if (!theErr)
- theErr = ResError();
- }
- ReleaseResource(resH);
- }
- }
-
- DisposePtr(specP);
- CloseResFile(resFileRef);
-
- return theErr;
- }
-
- //**********************************************************
- //
- // ChooseConnection - Poses Connection Settings dialog
- // allowing the user to choose and configure a tool
- //
- //**********************************************************
- static OSErr ChooseConnection(ConnHandle *hConn)
- {
- Point where;
- short result;
- Boolean returnVal;
-
- returnVal = kNothingChosenErr;
-
- if (*hConn) {
- SetPt(&where, 20, 40);
- HUnlock((Handle)(*hConn));
- result = CMChoose(hConn, where, nil);
- HLock((Handle)(*hConn));
- switch (result) {
- case chooseDisaster:
- case chooseFailed:
- returnVal = kNothingChosenErr;
- break;
- case chooseOKMinor:
- case chooseOKMajor:
- returnVal = noErr;
- break;
- default:
- returnVal = result;
- break;
- }
- }
- return returnVal;
- }
-
- //**********************************************************
- //
- // OpenDebugTerm - Open connection to the debugging terminal
- //
- //**********************************************************
- OSErr OpenDebugTerm(Boolean async, Boolean initCTB, Boolean prompt)
- {
- short procID;
- Handle configH;
- CMBufferSizes sizes;
- OSErr theErr;
- Boolean newTool;
-
- if (gConn)
- return kAlreadyOpenErr;
-
- if (initCTB)
- if (theErr = InitCTB())
- return theErr;
-
- // Find user's preferred tool and config string
- // or get defaults if blown or do not exist
- theErr = ReadPrefs(&procID, &configH);
- if (theErr == kToolNotAvailErr) {
- newTool = true;
- theErr = noErr; // if problem is tool changed,
- } else if (theErr) // make note, else pitch
- return theErr;
- else
- newTool = false;
-
- if (procID == -1)
- return kNoConnToolErr;
-
- sizes[cmDataIn] = 0; // Asking for zero means leave
- sizes[cmDataOut] = 0; // buffer size up to tool
- sizes[cmCntlIn] = 0;
- sizes[cmCntlOut] = 0;
- sizes[cmAttnIn] = 0;
- sizes[cmAttnOut] = 0;
-
- // Get a new Connection Tool record
- // and set config string as saved
- gConn = CMNew(procID, cmData, sizes, 0, 0);
- if (!gConn) {
- if (configH)
- DisposeHandle(configH);
- return kCMNewErr;
- } else if (configH) {
- HLock(configH); // don't let memory manager move
- CMSetConfig(gConn, *configH); // dereferenced pointer
- HUnlock(configH);
- DisposeHandle(configH);
- }
- MoveHHi((Handle)gConn);
- HLock((Handle)gConn); // lock to access data at interrupt level
-
- // pose Connection Settings dialog if desired or desired tool was
- // not available or configuration string was not available
- theErr = noErr;
- if (prompt || newTool || !configH)
- if (ChooseConnection(&gConn))
- theErr = kNothingChosenErr;
-
- // write resulting new preferences
- if (!theErr) {
- WritePrefs(gConn); // may want to complain
- theErr = CMOpen(gConn, async, CMCompletion, -1);
- }
-
- if (theErr) {
- if (gConn) // if ChooseDisaster, gConn already disposed & nil
- CMDispose(gConn);
- gConn = nil;
- return theErr;
- }
-
- gFlags = false;
- gAsyncCalls = async;
-
- return noErr;
- }
-
- //**********************************************************
- //
- // CloseDebugTerm - Close connection to debugging terminal
- //
- //**********************************************************
- OSErr CloseDebugTerm(void)
- {
- OSErr theErr;
- CMBufferSizes sizes;
- CMStatFlags statFlags;
-
- if (!gConn) // close only if opened
- return kNoConnectionErr; // or opening
-
- // close the connection, but only if it's open or trying to open
- theErr = CMStatus(gConn, sizes, &statFlags);
- if (statFlags & (cmStatusOpen + cmStatusOpening))
- theErr = CMClose(gConn, gAsyncCalls, CMCompletion, -1, false);
- HUnlock((Handle)gConn);
- CMDispose(gConn);
- gConn = nil;
-
- return theErr;
- }
-
- //**********************************************************
- //
- // DebugTerm - Sends pascal string out through connection
- //
- //**********************************************************
- OSErr DebugTerm(Str255 theStr)
- {
- long theSize;
- char *thePtr;
- OSErr theErr;
-
- if (!gConn)
- return kNoConnectionErr;
-
- // pass length byte as theSize and next byte as address
- theSize = *theStr;
- thePtr = (char *)theStr + 1;
- if (theErr = CMSendProc(thePtr, theSize, gFlags))
- return theErr;
-
- return noErr;
- }
-
- //**********************************************************
- //
- // debugf - Looks suspiciously like minprintf(),
- // found in K&R 2nd ed. page 156!
- //
- //**********************************************************
- OSErr debugf(char *fmt, ...)
- {
- va_list ap; // points to each unnamed argument
- char *p, *q, *cStr, *pStr;
- short len;
- Str255 s;
- long lVal;
- OSErr theErr;
- //Rect r;
-
- if (!gConn)
- return kNoConnectionErr;
-
- va_start(ap, fmt); // point ap to first unnamed argument
- for (p = fmt; *p; p++) {
- if (*p != '%') { // sluggish, but works
- if (theErr = CMSendProc(p, 1, gFlags))
- return theErr;
- continue;
- }
- switch(*++p) {
- case 'P':
- case 'p': // output a p string
- pStr = va_arg(ap, char *);
- len = *pStr;
- theErr = CMSendProc(++pStr, len, gFlags);
- break;
- case 'C':
- case 'c': // output a c string
- cStr = va_arg(ap, char *);
- for (len = 0, q = cStr; *q++ != '\0'; len++);
- theErr = CMSendProc(cStr, len, gFlags);
- break;
- case 'I': // treat int and short differently
- case 'i': // for compatibility
- lVal = va_arg(ap, int);
- NumToString(lVal, s);
- q = (char*)s;
- theErr = CMSendProc(++q, *s, gFlags);
- break;
- case 'S':
- case 's':
- lVal = va_arg(ap, short);
- NumToString(lVal, s);
- q = (char*)s;
- theErr = CMSendProc(++q, *s, gFlags);
- break;
- case 'L':
- case 'l': // output long integer
- NumToString(va_arg(ap, long), s);
- q = (char*)s;
- theErr = CMSendProc(++q, *s, gFlags);
- break;
- /*case 'R': // possible %r specifier
- case 'r':
- r = va_arg(ap, Rect);
- theErr = debugf("{%s, %s, %s, %s}",
- r.top, r.left, r.bottom, r.right);
- break;*/
- default:
- theErr = CMSendProc(p, 1, gFlags);
- break;
- }
- if (theErr) {
- va_end(ap);
- return theErr;
- }
- }
- va_end(ap);
- return noErr;
- }